using System;
using System.Collections.Generic;
using VisionarySystems.TimeRegi.Model;
using VisionarySystems.TimeRegi.Model.TimePeriods;
using System.Linq;

namespace VisionarySystems.TimeRegi.View.WorkTimeCalculator
{
	public class WorkTimeCalculatorFromTimeSlots : IWorkTimeCalculator
	{
		#region private members
		private readonly DateTime m_OverTimeStartDate;
		private readonly ICurrentTimeHandler m_CurrentTimeHandler;
		private ITimePeriode m_TimePeriode;
		private BreakTimeCalculator m_BreakTimeCalculator;
		#endregion

		#region Ctor

		public WorkTimeCalculatorFromTimeSlots(BreakTimeCalculator BreakTimeCalculator, DateTime OverTimeStartDate, ICurrentTimeHandler CurrentTimeHandler)
		{
			m_OverTimeStartDate = OverTimeStartDate;
			m_CurrentTimeHandler = CurrentTimeHandler;
			m_BreakTimeCalculator = BreakTimeCalculator;
		}

		#endregion

		#region properties

		public IEnumerable<ITimePeriode> WorkDays
		{
			get
			{
				return FindWorkDay(m_TimePeriode);
			}
		}

		private IEnumerable<ITimePeriode> FindWorkDay(ITimePeriode periodes)
		{
			foreach (ITimePeriode periode in periodes)
			{
				if (periode is WorkDay)
					yield return periode;
				else
					foreach (ITimePeriode p in FindWorkDay(periode))
						yield return p;
			}
		}

		#endregion

		#region public methods

		public TimeSpan BreakPerDay
		{
			get { return m_BreakTimeCalculator.BreakPerDay; }
		}

		public void LoadValues()
		{
			throw new NotImplementedException();
		}

		public void FillValues(ITimePeriode TimePeriode)
		{
			m_TimePeriode = TimePeriode;
		}

		public TimeSpan GetTime(DateTime StartDate, DateTime EndDate, bool AddDaysOff = false, bool RemoveBreakTime = true)
		{
			// Get all timeslots for that periode
			ITimePeriode currentTimePeriode = new TimePeriodModel();
			m_TimePeriode.GetTimePeriode(StartDate, EndDate, ref currentTimePeriode);

			// Calc work time
			TimeSpan currentWorkedTime = currentTimePeriode.WorkedTime;

			// Calc breakTime
			List<DateTime> workingDates = new List<DateTime>();
			currentTimePeriode.GetWorkingDates(ref workingDates);
			TimeSpan currentBrakeTime;
			if (RemoveBreakTime)
				currentBrakeTime = m_BreakTimeCalculator.GetBrakeTime(workingDates);
			else
				currentBrakeTime = new TimeSpan();

			TimeSpan daysOffTime = new TimeSpan();
			if (AddDaysOff)
			{
				List<DateTime> daysOff = getDaysOff(StartDate, EndDate, workingDates);
				foreach (var day in daysOff)
					daysOffTime = daysOffTime.Add(DaysOffCalculator.ExpectedWorkHoursPerDay(day.DayOfWeek));
			}

			// Calc result
			return currentWorkedTime.Subtract(currentBrakeTime).Add(daysOffTime);
		}

		public TimeSpan ExpectedWorkHoursPerDay(DayOfWeek TheDay)
		{
			return DaysOffCalculator.ExpectedWorkHoursPerDay(TheDay);
		}

		public void AddDay(DateTime ThisDate, TimeSpan Value)
		{
			throw new NotImplementedException();
		}

		public void Clear()
		{
			throw new NotImplementedException();
		}

		public TimeSpan GetBreakTime(DateTime ThisDate)
		{
			throw new NotImplementedException();
		}

		public TimeSpan GetOverTime()
		{
			TimeSpan result = new TimeSpan();

			var overTimeDays = from day in WorkDays
							   let workDay = day as WorkDay
							   where workDay != null &&
							   workDay.WorkDate >= m_OverTimeStartDate &&
							   workDay.WorkDate < m_CurrentTimeHandler.StartTime.Date
							   select day;
			foreach (WorkDay day in overTimeDays)
			{
				TimeSpan actualWorkedTime = GetTime(day.WorkDate, day.WorkDate.AddDays(1).AddSeconds(-1));
				TimeSpan expectedWork = ExpectedWorkHoursPerDay(day.WorkDate.DayOfWeek);
				result = result.Add(actualWorkedTime.Subtract(expectedWork));
			}
			return result;
		}

		public void Add(ITimePeriode TimePeriode)
		{
			throw new NotImplementedException();
		}

		#endregion

		private List<DateTime> getDaysOff(DateTime StartDate, DateTime EndDate, List<DateTime> WorkingDates)
		{
			List<DateTime> result = new List<DateTime>();
			List<DateTime> allDays = new List<DateTime>();

			// Get a list of all possible working days
			DateTime currentDate = StartDate.Date;
			for (int dayNo = 1; currentDate <= EndDate; dayNo++)
			{
				if ((currentDate.DayOfWeek != DayOfWeek.Saturday) || (currentDate.DayOfWeek != DayOfWeek.Sunday))
					allDays.Add(currentDate);
				currentDate = StartDate.Date.AddDays(dayNo);
			}

			// Find days off
			foreach (var day in allDays)
			{
				if (!WorkingDates.Contains(day))
					result.Add(day);
			}

			return result;

		}
	}
}